#![allow(dead_code)]

use std::fmt::{self, Display, Formatter};

//多处用到, 由花色和点数合成牌ID, 封成一个宏
//用宏还有一个好处，可以用as强制转换类型
//方便的接受多种类型参数
#[macro_export]
macro_rules! sn2poker {
    ($suit:expr, $num:expr) => {
        PokerCard::from_suit_num($suit as u8, $num as u8)
    };
}

//黑桃,红心,草花,方片
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum PokerCard {
    Spade(u8),
    Heart(u8),
    Club(u8),
    Diamond(u8),
}

impl PokerCard {
    //52张牌用1～52序号表示
    pub fn from_u8(v: u8) -> Result<Self, String> {
        if v < 1 || v > 52 {
            return Err(String::from(format!("invaild cards:{:?}", v)));
        }
        let n = (v - 1) % 13 + 1;
        let tv = match (v - 1) / 13 {
            0 => PokerCard::Spade(n),
            1 => PokerCard::Heart(n),
            2 => PokerCard::Club(n),
            3 => PokerCard::Diamond(n),
            _ => return Err(String::from(format!("invaild cards:{:?}", v))),
        };
        Ok(tv)
    }

    //百位表示花色,余数表示点数,对人类友好
    //例如：101表示黑桃A
    pub fn from_u16(v: u16) -> Result<Self, String> {
        let v8 = ((v / 100 - 1) * 13) as u8 + (v % 100) as u8;
        Self::from_u8(v8)
    }

    pub fn from_suit_num(t: u8, n: u8) -> Result<Self, String> {
        if n > 14 || n < 1 {
            return Err(String::from(format!("invaild card num:{:?}", n)));
        }
        let cn = if n==14 {1} else {n};
        let tv = match t {
            0 => PokerCard::Spade(cn),
            1 => PokerCard::Heart(cn),
            2 => PokerCard::Club(cn),
            3 => PokerCard::Diamond(cn),
            _ => return Err(String::from(format!("invaild card suit:{:?}", t))),
        };
        Ok(tv)
    }

    pub fn to_u8(&self) -> u8 {
        match self {
            PokerCard::Spade(n) => *n,
            PokerCard::Heart(n) => 13 + *n,
            PokerCard::Club(n) => 26 + *n,
            PokerCard::Diamond(n) => 39 + *n,
        }
    }

    pub fn get_suit_num(&self) -> (u8, u8) {
        match self {
            PokerCard::Spade(n) => (0, *n),
            PokerCard::Heart(n) => (1, *n),
            PokerCard::Club(n) => (2, *n),
            PokerCard::Diamond(n) => (3, *n),
        }
    }
}

impl Display for PokerCard {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let tt = ["♠", "♥", "♣", "♦"];
        let nn = [
            "", "A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K",
        ];
        let (t, n) = self.get_suit_num();
        write!(f, "{} {}", tt[t as usize], nn[n as usize])
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn it_works() {
        let result = PokerCard::from_u8(2 + 2).unwrap();
        let (t, n) = result.get_suit_num();
        assert_eq!(n, 4);
        assert_eq!(t, 0);
    }
}
